// CS 564:: PROJECT STAGE 5 - Catalog
//
// Caela Northey (caela) 905 653 2238
// Colin Laska   (laska) 906 150 7258
//
// For this part of the project, we will implement the front-end and
// database utilities of Minirel. We will also design and implement
// the Minirel catalog relations. A command parser was provided. We
// made changes to the following functions:
//
//  RelCatalog:  getInfo(), addInfo(), removeInfo()
//  AttrCatalog: getInfo(), addInfo(), getRelInfo()
//
// NOTE: createRel(), destroyRel(), and help() are implemented
//       in sperate files

#include "catalog.h"

RelCatalog::RelCatalog(Status &status) :
	 HeapFile(RELCATNAME, status)
{
// nothing should be needed here
}

// 1) Open a scan on the relcat relation by invoking the startScan()
// method on itself. Look for the tuple whose first attribute matches
// the string relName.
// 2) call scanNext() and getRecord() to get the desired tuple
// 3) you need to memcpy() the tuple out of the buffer pool into the
// return parameter record.
const Status RelCatalog::getInfo(const string & relation, RelDesc &record)
{
  if (relation.empty())
    return BADCATPARM;

  Status status;
  Record rec;
  RID rid;
  HeapFileScan *hfs;

  hfs = new HeapFileScan(RELCATNAME, status);
	if (status != OK){
    return status;
  }

  //Recall: startScan(const int offset, const int length, const Datatype
  //        type, const char* filter, const Operator op);
  // SO:  offset   = 0 b/c first attribute in tuple
  //      lengh    = sizeof(relation) b/c match will be that much data
  //      Datatype = STRING (b/c not INT or FLOAT)
  //      filter   = relation b/c what we want to match
  //      op       = EQ b/c want them to match
  status = hfs->startScan(0, (int)sizeof(relation), STRING, relation.c_str(), EQ);
  if (status != OK){
    return status;
  }

  //Recall: const Status HeapFileScan::scanNext(RID& outRid)
  // returns RID of next (in this case, FIRST) record that
  // statisfies scan predicate
  status = hfs->scanNext(rid);	
  if (status != OK){
    return RELNOTFOUND; // NEED TO RETURN NOT IN DATABASE CODE
  }
  //curRec and rid now hold desired RID

  //Recall: const Status getRecord(Record&rec)
  // returns record whose rid is stored in curRec, which was pinned
  // there by a preceding scanNext() call
  status = hfs->getRecord(rec);
  if (status != OK){
    return status;
  }

  //Recall: void * memcpy(void * dest, const void * src, size_t n); 
  //returns pointer to dest
  memcpy(&record, rec.data, rec.length);

  //check if another exists w anther scanNext()?
  return status;
}

#include <stdlib.h>

// Adds the relation descriptor contained in record to the relcat
// relation. RelDesc represents both the in-memory format and on-disk
// format of a tuple in relcat.
//
// 1) create an InsertFileScan object on the relation catalog table
// 2) create a record and then insert it into the relation catalog
// table using the method insertRecord of InsertFileScan.
const Status RelCatalog::addInfo(RelDesc & record)
{

  RID rid;
  InsertFileScan*  ifs;
  Status status;
  Record rec;

  //create a rec. Recall:
  // struct Record {
  //   void* data;
  //   int length;
  // };
  rec.data = &record; 
  rec.length = sizeof(record);

  ifs = new InsertFileScan(RELCATNAME,status);
  if (status != OK){
    return status;
  }

  //Recall: const Status insertRecord (const Record & rec, RID& outRid)
  status = ifs->insertRecord(rec, rid);
  if (status != OK){
    return status;
  }

  delete ifs;

  return status; //should be OK by here
}

// Remove the tuple corresponding to relName from relcat.
// 1) start a filter scan on relcat to locate the RID of the
// desired tuple. 
// 2) call deleteRecord() to remove it.
const Status RelCatalog::removeInfo(const string & relation)
{

//very similar to RelCatalog::getInfo()...

  Status status;
  RID rid;
  HeapFileScan*  hfs;

  if (relation.empty()) return BADCATPARM;

  hfs = new HeapFileScan(RELCATNAME, status);
	if (status != OK){
    return status;
  }

  status = hfs->startScan(0, (int)sizeof(relation), STRING, relation.c_str(), EQ);
  if (status != OK){
    return status;
  }

  status = hfs->scanNext(rid);	
  if (status == FILEEOF){
    return RELNOTFOUND;
  }

  else if (status != OK){
    return status;
  }
  //curRec and rid now hold desired RID

  // except for:

  //Recall: const Status deleteRecord() deletes record in curRec
  //which was placed there by a preceding scanNext() call
  status = hfs->deleteRecord();
  if (status != OK){
    return status;
  }

  return status; //should be OK by here
}


RelCatalog::~RelCatalog()
{
// nothing should be needed here
}


AttrCatalog::AttrCatalog(Status &status) :
	 HeapFile(ATTRCATNAME, status)
{
// nothing should be needed here
}

// -----------------------------------------------------------------------

// Returns the attribute descriptor record for attribute attrName in
// relation relName. 
//
// 1) scan over the underlying heapfile to get all tuples for relation
// 2) check each tuple to find whether it corresponds to attrName. 
//    (Or maybe do it the other way around !) 
const Status AttrCatalog::getInfo(const string & relation, 
				  const string & attrName,
				  AttrDesc &record)
{
  Status status;
  RID rid;
  Record rec;
  HeapFileScan*  hfs;

  if (relation.empty() || attrName.empty()) return BADCATPARM;

  hfs = new HeapFileScan(ATTRCATNAME, status);
  if (status != OK){
    return status;
  }

  status = hfs->startScan(0, (int)sizeof(relation), STRING, relation.c_str(), EQ);

  //all tuples: scanNext until retuners EOF
  //(will terminate when scanNext reaches EOF and status != OK)
  while(1){
    status = hfs->scanNext(rid);   //puts next matching tuple in curRec

    if(status == FILEEOF){
      return ATTRNOTFOUND;
    }
    else if (status != OK){
      break;
    }

    status = hfs->getRecord(rec);  //and gets that tuple into rec
    if (status != OK){
      break;
    }
    AttrDesc * ad = (AttrDesc *) rec.data; //tuple is an AttrDesc

    if(strcmp(ad->attrName, attrName.c_str()) == 0){
      memcpy(&record, rec.data, rec.length);
      return OK;
    }

  } //end while

  return status; //attrName not found
}

// Adds a tuple (corresponding to an attribute of a relation) to
// the attrcat relation.
const Status AttrCatalog::addInfo(AttrDesc & record)
{
  RID rid;
  InsertFileScan*  ifs;
  Status status;
  Record rec;

  rec.data = &record;
  rec.length = sizeof(record);

  ifs = new InsertFileScan(ATTRCATNAME, status);
  if (status != OK){
    return status;
  }

  //Recall: const Status insertRecord (const Record & rec, RID& outRid)
  status = ifs->insertRecord(rec, rid);
  if (status != OK){
    cout << "ERROR CODE: " << status << endl;

    return status;
  }

  delete ifs;

  return status; //should be OK by here
}

//Removes the tuple from attrcat that corresponds to attribute attrName
//of relation.
const Status AttrCatalog::removeInfo(const string & relation, 
			       const string & attrName)
{
  Status status;
  Record rec;
  RID rid;
  AttrDesc record;
  HeapFileScan*  hfs;

  if (relation.empty() || attrName.empty()) return BADCATPARM;

  hfs = new HeapFileScan(ATTRCATNAME, status);
  if (status != OK){
    return status;
  }

  status = hfs->startScan(0, (int)sizeof(relation), STRING, relation.c_str(), EQ);

  //all tuples: scanNext until retunrs EOF
  while(1){

    status = hfs->scanNext(rid);   //puts next matching tuple in curRec
    if (status != OK){
      break;
    }

    status = hfs->getRecord(rec);  //and gets that tuple into rec
    if (status != OK){
      break;
    }

    record = *((AttrDesc *) rec.data); //tuple is an AttrDesc

    if(strcmp(record.attrName, attrName.c_str()) == 0){
      status = hfs->deleteRecord(); //deletes curRec
      return status;
    }

  } //end while

  return status; //attrName not found
}

// While getInfo() above returns the description of a single attribute,
// this method returns (by reference) descriptors for all attributes of
// the relation via attr, an array of AttrDesc structures,  and the 
// count of the number of attributes in attrCnt. The attrs array is
// allocated by this function, but it should be deallocated by the caller.
const Status AttrCatalog::getRelInfo(const string & relation, 
				     int &attrCnt,
				     AttrDesc *&attrs)
{
  Status status;
  RID rid;
  Record rec;
  HeapFileScan*  hfs;
  int count = 0;

  if (relation.empty()) return BADCATPARM;

  hfs = new HeapFileScan(ATTRCATNAME, status);
  if (status != OK){
    return status;
  }

  status = hfs->startScan(0, (int)sizeof(relation), STRING, relation.c_str(), EQ);
  if (status != OK){
    return status;
  }

  status = hfs->markScan(); // check point
  if (status != OK){
    return status;
  }

  //LOOP 1: count number matches for array allocation
  //when encounters a match, counter++ 
  while(1){
    status = hfs->scanNext(rid);   //puts next matching tuple in curRec
    if(status == FILEEOF){
      break;
    }
    else if (status != OK){
      break;
    }
    count++;
  }

  attrs = new AttrDesc[count];
  status = hfs->resetScan();   //restore to prev check point

  if (status != OK){
    return status;
  }

  //LOOP 2: fill array
  //when encounters a match, adds it to the array of AttrDesc's
  for (int i = 0; i < count; i++){
    status = hfs->scanNext(rid);   //puts next matching tuple in curRec
    if(status == FILEEOF){
      break;
    }
    else if (status != OK){
      break;
    }

    status = hfs->getRecord(rec);  //and gets that tuple into rec
    if (status != OK){
      break;
    }
    AttrDesc * ad = (AttrDesc *) rec.data; //tuple is an AttrDesc

    attrs[i] = *ad;

  }

  attrCnt = count;
  return status;
}

AttrCatalog::~AttrCatalog()
{
// nothing should be needed here
}
